//
// Created by Scave on 2023/11/13.
//

#include "Renderer.h"
#include "bx/math.h"
#include "component/BasicComponents.h"
#include "component/InternalComponent.h"
#include "Lazy.h"

NS_RIDER_BEGIN

    static Vertex s_TestCubeVertices[] = {
            // front
            {+0.5f, +0.5f, +0.5f, Vectors::Forward, {0, 0}, Colors::Red},
            {-0.5f, +0.5f, +0.5f, Vectors::Forward, {0, 0}, Colors::Red},
            {-0.5f, -0.5f, +0.5f, Vectors::Forward, {0, 0}, Colors::Red},
            {+0.5f, -0.5f, +0.5f, Vectors::Forward, {0, 0}, Colors::Red},
            // right
            {-0.5f, +0.5f, +0.5f, Vectors::Right, {0, 0}, Colors::Green},
            {-0.5f, +0.5f, -0.5f, Vectors::Right, {0, 0}, Colors::Green},
            {-0.5f, -0.5f, -0.5f, Vectors::Right, {0, 0}, Colors::Green},
            {-0.5f, -0.5f, +0.5f, Vectors::Right, {0, 0}, Colors::Green},
            // back
            {-0.5f, +0.5f, -0.5f, Vectors::Back, {0, 0}, Colors::Blue},
            {+0.5f, +0.5f, -0.5f, Vectors::Back, {0, 0}, Colors::Blue},
            {+0.5f, -0.5f, -0.5f, Vectors::Back, {0, 0}, Colors::Blue},
            {-0.5f, -0.5f, -0.5f, Vectors::Back, {0, 0}, Colors::Blue},
            // left
            {+0.5f, +0.5f, -0.5f, Vectors::Left, {0, 0}, Colors::Orange},
            {+0.5f, +0.5f, +0.5f, Vectors::Left, {0, 0}, Colors::Orange},
            {+0.5f, -0.5f, +0.5f, Vectors::Left, {0, 0}, Colors::Orange},
            {+0.5f, -0.5f, -0.5f, Vectors::Left, {0, 0}, Colors::Orange},
            // top
            {+0.5f, +0.5f, -0.5f, Vectors::Up, {0, 0}, Colors::Purple},
            {-0.5f, +0.5f, -0.5f, Vectors::Up, {0, 0}, Colors::Purple},
            {-0.5f, +0.5f, +0.5f, Vectors::Up, {0, 0}, Colors::Purple},
            {+0.5f, +0.5f, +0.5f, Vectors::Up, {0, 0}, Colors::Purple},
            // bottom
            {+0.5f, -0.5f, +0.5f, Vectors::Down, {0, 0}, Colors::Cyan},
            {-0.5f, -0.5f, +0.5f, Vectors::Down, {0, 0}, Colors::Cyan},
            {-0.5f, -0.5f, -0.5f, Vectors::Down, {0, 0}, Colors::Cyan},
            {+0.5f, -0.5f, -0.5f, Vectors::Down, {0, 0}, Colors::Cyan},
    };
    static const uint16_t s_TestCubeTriList[] = {
            // front
            0, 2, 1, 0, 3, 2,
            // right
            4, 6, 5, 4, 7, 6,
            // back
            8, 10, 9, 8, 11, 10,
            // left
            12, 14, 13, 12, 15, 14,
            // top
            16, 18, 17, 16, 19, 18,
            // bottom
            20, 22, 21, 20, 23, 22
    };
    static bgfx::VertexBufferHandle s_vbuf = bgfx::VertexBufferHandle{bgfx::kInvalidHandle};
    static bgfx::IndexBufferHandle s_ibuf = bgfx::IndexBufferHandle{bgfx::kInvalidHandle};


    MeshRenderer::MeshRenderer(const SMART_PTR<RenderContext>& context) : Renderer(context) {
        this->program_ = MAKE_SMART_PTR<StandardShaderProgram>();
    }

    void MeshRenderer::Render(SMART_PTR<RenderNode> node) {
        SMART_PTR<StandardRenderNode> r_node = STATIC_CAST<StandardRenderNode>(node);
        UInt16 layer_id = static_cast<UInt16>(node->layer_mask);

        SetShaderParams(r_node);
        bgfx::setTransform(r_node->mtx_model.Data());

        /*BufferHandle* handle = context_->GetBufferHandle(node->name);
        if (handle == nullptr) {
            Mesh* mesh = r_node->mesh;
            handle = context_->CreateOrUpdateBuffer(
                    r_node->name,
                    program_,
                    mesh->vertex_data_list,
                    mesh->vertex_index_list
            );
        }
        bgfx::setVertexBuffer(0, handle->vertex_buffer_static);
        bgfx::setIndexBuffer(handle->index_buffer_static);*/
        if (!bgfx::isValid(s_vbuf)) {
            const bgfx::Memory* v_ref = bgfx::makeRef(s_TestCubeVertices, sizeof(s_TestCubeVertices));
            s_vbuf = bgfx::createVertexBuffer(v_ref, program_->vertex_layout_);
        }
        if (!bgfx::isValid(s_ibuf)) {
            const bgfx::Memory* i_ref = bgfx::makeRef(s_TestCubeTriList, sizeof(s_TestCubeTriList));
            s_ibuf = bgfx::createIndexBuffer(i_ref);
        }
        bgfx::setVertexBuffer(0, s_vbuf);
        bgfx::setIndexBuffer(s_ibuf);

        bgfx::setViewFrameBuffer(layer_id, BGFX_INVALID_HANDLE);
        bgfx::setState(
                BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_WRITE_Z
                | BGFX_STATE_DEPTH_TEST_LESS | BGFX_STATE_CULL_CW | BGFX_STATE_MSAA
                | RenderTypeToBgfxState(r_node->render_type)
        );
        bgfx::submit(layer_id, program_->program_handle_);
    }

    void MeshRenderer::SetShaderParams(SMART_PTR<StandardRenderNode>& node) {
        vec4_camera_pos_[0] = node->camera_pos.x;
        vec4_camera_pos_[1] = node->camera_pos.y;
        vec4_camera_pos_[2] = node->camera_pos.z;

        if (node->material.main_texture.IsValid()) {
            vec4_material_params_[0] = 1;
            program_->SetTexture(0, TEXTURE_MAIN, node->material.main_texture);
        } else {
            vec4_material_params_[0] = -1;
        }
        if (node->material.specular_texture.IsValid()) {
            vec4_material_params_[1] = 1;
            program_->SetTexture(1, TEXTURE_SPECULAR, node->material.specular_texture);
        } else {
            vec4_material_params_[1] = -1;
        }
        vec4_material_params_[2] = node->material.smoothness;
        program_->SetUniformV4(UNIFORM_MATERIAL_PARAMS, vec4_material_params_);
        SetLightsInternal(node);
    }

    void MeshRenderer::SetLightsInternal(SMART_PTR<StandardRenderNode>& node) {
        for (int i = 0; i < node->lights.size(); ++i) {
            LightItem item = node->lights[i];
            int base = i * 6 * 4;
            // params
            this->vec4_arr_lights_[base][0] = static_cast<float>(item.type);
            this->vec4_arr_lights_[base][1] = item.strength;
            // position
            Vec3 world_pos = item.position;
            this->vec4_arr_lights_[base + 1][0] = world_pos.x;
            this->vec4_arr_lights_[base + 1][1] = world_pos.y;
            this->vec4_arr_lights_[base + 1][2] = world_pos.z;
            // direction
            Vec3 direction = item.direction;
            this->vec4_arr_lights_[base + 2][0] = direction.x;
            this->vec4_arr_lights_[base + 2][1] = direction.y;
            this->vec4_arr_lights_[base + 2][2] = direction.z;
            // color
            Color color = item.color;
            this->vec4_arr_lights_[base + 3][0] = color.r;
            this->vec4_arr_lights_[base + 3][1] = color.g;
            this->vec4_arr_lights_[base + 3][2] = color.b;
            this->vec4_arr_lights_[base + 3][3] = color.a;
            // attenuation
            LightAttenuation attenuation = item.attenuation;
            float range = item.range;
            this->vec4_arr_lights_[base + 4][0] = attenuation.constant;
            this->vec4_arr_lights_[base + 4][1] = attenuation.linear;
            this->vec4_arr_lights_[base + 4][2] = attenuation.quadratic;
            this->vec4_arr_lights_[base + 4][3] = range;
            // spotParams
            this->vec4_arr_lights_[base + 5][0] = item.spotAngle;
            this->vec4_arr_lights_[base + 5][1] = item.spotAngle;
            this->vec4_arr_lights_[base + 5][2] = 0.01;
        }

        vec4_light_count_[0] = node->lights.size();
        vec4_ambient_color_[0] = node->ambient_color.r;
        vec4_ambient_color_[1] = node->ambient_color.g;
        vec4_ambient_color_[2] = node->ambient_color.b;
        vec4_ambient_color_[3] = node->ambient_color.a;
        program_->SetUniformV4(UNIFORM_AMBIENT_COLOR, vec4_ambient_color_);
        program_->SetUniformV4(UNIFORM_LIGHT_COUNT, vec4_light_count_);
        program_->SetUniformV4Array(UNIFORM_LIGHTS, vec4_arr_lights_, MAX_LIGHTS * 6);
    }

NS_RIDER_END
